package com.epam.grsulab.webapi.dao.base.impl;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;

import org.springframework.transaction.annotation.Transactional;

import com.epam.grsulab.webapi.dao.base.Dao;
import com.epam.grsulab.webapi.entity.base.BaseEntity;

/**
 * @author Dmitry_Zhivushko
 * 
 * @param <T>
 */
public abstract class AbstractDaoImpl<T extends BaseEntity> implements Dao<T> {

	private final Class<T> domainClass;

	private EntityManager em;
	
	@PersistenceContext
	public void setEntityManager(final EntityManager em) {
		this.em = em;
	}

	protected EntityManager getEntityManager() {
		return em;
	}

	public AbstractDaoImpl(final Class<T> domainClass) {
		this.domainClass = domainClass;
	}

	@Transactional
	public void delete(final T object) {
		getEntityManager().remove(object);
	}
	
	@Transactional
	public void delete(final Serializable id) {
		getEntityManager().remove(getEntityManager().getReference(getDomainClass(), id));
	}

	public T load(final Serializable id) {
		return getEntityManager().find(getDomainClass(), id);
	}

	@Transactional
	public T save(final T object) {
		return getEntityManager().merge(object);
	}

	public List<T> findAll() {
		final CriteriaQuery<? extends BaseEntity> query = getEntityManager().getCriteriaBuilder().createQuery(getDomainClass());
		query.from(getDomainClass());
		@SuppressWarnings("unchecked")
		final List<T> lst = (List<T>) getEntityManager().createQuery(query).getResultList();
		return lst;
	}

	public Long countAll() {
		final CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
		CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
		final Expression<Long> countFrom = builder.count(countQuery.from(getDomainClass()));
		countQuery = countQuery.select(countFrom);
		final TypedQuery<Long> typedCountQuery = getEntityManager().createQuery(countQuery);
		return  typedCountQuery.getSingleResult();
	}

	public Class<T> getDomainClass() {
		return domainClass;
	}
}
